﻿/* -*- c++ -*- */
#ifndef PLOTTER_H
#define PLOTTER_H

#include <QtGui>
#include <QFont>
#include <QFrame>
#include <QImage>
#include <vector>
#include <QMap>

#define HORZ_DIVS_MAX 12    //50
#define VERT_DIVS_MIN 5
#define MAX_SCREENSIZE 16384

#define PEAK_CLICK_MAX_H_DISTANCE 10 //Maximum horizontal distance of clicked point from peak
#define PEAK_CLICK_MAX_V_DISTANCE 20 //Maximum vertical distance of clicked point from peak
#define PEAK_H_TOLERANCE 2


class CPlotter : public QFrame
{
	Q_OBJECT

public:
	explicit CPlotter(QWidget *parent = 0);
	~CPlotter();

	QSize minimumSizeHint() const;
	QSize sizeHint() const;

	//Size limit approach. When resolution is too large, update rate will be too slow without this approach.
	QSize pixSize();
	double pixRatio() const{return m_dPixRatio;}

	QSize maxPixmapSize() const {return m_maxSize;}
	void  setMaxPixmapSize(QSize s){ m_maxSize = s;}
	void draw();		//call to draw new fft data onto screen plot
	void setRunningState(bool running) { m_Running = running; }
	void setClickResolution(int clickres) { m_ClickResolution = clickres; }
	void setFilterClickResolution(int clickres) { m_FilterClickResolution = clickres; }
	void setFilterBoxEnabled(bool enabled) { m_FilterBoxEnabled = enabled; }
	void setCenterLineEnabled(bool enabled) { m_CenterLineEnabled = enabled; }
	void setTooltipsEnabled(bool enabled) { m_TooltipsEnabled = enabled; }
	void setBookmarksEnabled(bool enabled) { m_BookmarksEnabled = enabled; }

	void setNewFftData(const double *fftData, int size);
	void setNewFftData(const double *fftData, const double *wfData, int size);

	void setCenterFreq(quint64 f);
	qint64 getCenterFreq() const {return m_CenterFreq;}
	void setFreqUnits(qint32 unit) { m_FreqUnits = unit; }

	void setDemodCenterFreq(quint64 f) { m_DemodCenterFreq = f; }

	/*! \brief Move the filter to freq_hz from center. */
	void setFilterOffset(qint64 freq_hz)
	{
		m_DemodCenterFreq = m_CenterFreq + freq_hz;
		drawOverlay();
	}
	qint64 getFilterOffset(void)
	{
		return m_DemodCenterFreq - m_CenterFreq;
	}

	int getFilterBw()
	{
		return m_DemodHiCutFreq - m_DemodLowCutFreq;
	}

	void setHiLowCutFrequencies(int LowCut, int HiCut)
	{
		m_DemodLowCutFreq = LowCut;
		m_DemodHiCutFreq = HiCut;
		drawOverlay();
	}

	void getHiLowCutFrequencies(int *LowCut, int *HiCut)
	{
		*LowCut = m_DemodLowCutFreq;
		*HiCut = m_DemodHiCutFreq;
	}

	void setDemodRanges(int FLowCmin, int FLowCmax, int FHiCmin, int FHiCmax, bool symetric);

	/* Shown bandwidth around SetCenterFreq() */
	void setSpanFreq(quint32 s)
	{
		if (s > 0 && s < INT_MAX) {
			m_Span = (qint32)s;
			setFftCenterFreq(m_FftCenter);
		}
		drawOverlay();
	}

	qint64 getSpanFreq() const {return m_Span;}

	void setMaxBandiwdth(int m){m_FLowCmin = -m/2; m_FHiCmax = m/2; }


	void setHdivDelta(int delta) { m_HdivDelta = delta; }
	void setVdivDelta(int delta) { m_VdivDelta = delta; }

	void setFreqDigits(int digits) { m_FreqDigits = digits>=0 ? digits : 0; }

	/* Determines full bandwidth. */
	void setSampleRate(double rate)
	{
		if (rate > 0.0)
		{
			m_SampleFreq = rate;
			drawOverlay();
		}
	}

	double getSampleRate(void)
	{
		return m_SampleFreq;
	}

	void setFftCenterFreq(qint64 f) {
		qint64 limit = ((qint64)m_SampleFreq + m_Span) / 2 - 1;
		m_FftCenter = qBound(-limit, f, limit);
	}

	int     getNearestPeak(QPoint pt);
	void    setWaterfallSpan(quint64 span_ms);
	quint64 getWfTimeRes(void);
	void    setFftRate(int rate_hz);
	void    clearWaterfall(void);
	bool    saveWaterfall(const QString & filename) const;

signals:
	void newCenterFreq(qint64 f);
	void newDemodFreq(qint64 freq, qint64 delta); /* delta is the offset from the center */
	void newLowCutFreq(int f);
	void newHighCutFreq(int f);
	void newFilterFreq(int low, int high);  /* substitute for NewLow / NewHigh */
	void pandapterRangeChanged(double min, double max);
	void newZoomLevel(double level);

public slots:
	// zoom functions
	void resetHorizontalZoom(void);
	void moveToCenterFreq(void);
	void moveToDemodFreq(void);
	void zoomOnXAxis(double level);

	// other FFT slots
	void setFftPlotColor(const QColor color);
	void setFftFill(bool enabled);
	void setPeakHold(bool enabled);
	void setFftRange(double min, double max);
	void setPandapterRange(double min, double max);
	void setWaterfallRange(double min, double max);
	void setPeakDetection(bool enabled, double c);
	void updateOverlay();

	void setPercent2DScreen(int percent)
	{
		m_Percent2DScreen = percent;
		m_Size = QSize(0,0);
		resizeEvent(NULL);
	}

protected:
	//re-implemented widget event handlers
	void paintEvent(QPaintEvent *event);
	void resizeEvent(QResizeEvent* event);
	void mouseMoveEvent(QMouseEvent * event);
	void mousePressEvent(QMouseEvent * event);
	void mouseReleaseEvent(QMouseEvent * event);
	void wheelEvent( QWheelEvent * event );

private:
	enum eCapturetype {
		NOCAP,
		LEFT,
		CENTER,
		RIGHT,
		YAXIS,
		XAXIS,
		BOOKMARK
	};

	void        drawOverlay();
	void        makeFrequencyStrs();
	int         xFromFreq(qint64 freq);
	qint64      freqFromX(int x);
	void        zoomStepX(double factor, int x);
	qint64      roundFreq(qint64 freq, int resolution);
	quint64     msecFromY(int y);
	void        clampDemodParameters();
	bool        isPointCloseTo(int x, int xr, int delta)
	{
		return ((x > (xr - delta)) && (x < (xr + delta)));
	}
	void getScreenIntegerFFTData(qint32 plotHeight, qint32 plotWidth,
								 double maxdB, double mindB,
								 qint64 startFreq, qint64 stopFreq,
								 const double *inBuf, qint32 *outBuf,
								 qint32 *maxbin, qint32 *minbin);
	void calcDivSize (qint64 low, qint64 high, int divswanted, qint64 &adjlow, qint64 &step, int& divs);

	bool        m_PeakHoldActive;
	bool        m_PeakHoldValid;
	qint32      m_fftbuf[MAX_SCREENSIZE];
	quint8      m_wfbuf[MAX_SCREENSIZE]; // used for accumulating waterfall data at high time spans
	qint32      m_fftPeakHoldBuf[MAX_SCREENSIZE];
	const double      *m_fftData;     /*! pointer to incoming FFT data */
	const double      *m_wfData;
	int         m_fftDataSize;

	int         m_XAxisYCenter;
	int         m_YAxisWidth;

	eCapturetype    m_CursorCaptured;
	QPixmap     m_2DPixmap;
	QPixmap     m_OverlayPixmap;
	QPixmap     m_WaterfallPixmap;
	QColor      m_ColorTbl[256];
	QSize       m_Size;
	QString     m_Str;
	QString     m_HDivText[HORZ_DIVS_MAX+1];
	bool        m_Running;
	bool        m_DrawOverlay;
	qint64      m_CenterFreq;       // The HW frequency
	qint64      m_FftCenter;        // Center freq in the -span ... +span range
	qint64      m_DemodCenterFreq;
	qint64      m_StartFreqAdj;
	qint64      m_FreqPerDiv;
	bool        m_CenterLineEnabled;  /*!< Distinguish center line. */
	bool        m_FilterBoxEnabled;   /*!< Draw filter box. */
	bool        m_TooltipsEnabled;     /*!< Tooltips enabled */
	bool        m_BookmarksEnabled;   /*!< Show/hide bookmarks on spectrum */
	int         m_DemodHiCutFreq;
	int         m_DemodLowCutFreq;
	int         m_DemodFreqX;		//screen coordinate x position
	int         m_DemodHiCutFreqX;	//screen coordinate x position
	int         m_DemodLowCutFreqX;	//screen coordinate x position
	int         m_CursorCaptureDelta;
	int         m_GrabPosition;
	int         m_Percent2DScreen;

	int         m_FLowCmin;
	int         m_FLowCmax;
	int         m_FHiCmin;
	int         m_FHiCmax;
	bool        m_symetric;

	int         m_HorDivs;   /*!< Current number of horizontal divisions. Calculated from width. */
	int         m_VerDivs;   /*!< Current number of vertical divisions. Calculated from height. */

	double       m_PandMindB;
	double       m_PandMaxdB;
	double       m_WfMindB;
	double       m_WfMaxdB;

	qint64      m_Span;
	double       m_SampleFreq;    /*!< Sample rate. */
	qint32      m_FreqUnits;
	int         m_ClickResolution;
	int         m_FilterClickResolution;

	int         m_Xzero;
	int         m_Yzero;  /*!< Used to measure mouse drag direction. */
	int         m_FreqDigits;  /*!< Number of decimal digits in frequency strings. */

	QFont       m_Font;         /*!< Font used for plotter (system font) */
	int         m_HdivDelta; /*!< Minimum distance in pixels between two horizontal grid lines (vertical division). */
	int         m_VdivDelta; /*!< Minimum distance in pixels between two vertical grid lines (horizontal division). */

	quint32     m_LastSampleRate;

	QColor      m_FftColor, m_FftFillCol, m_PeakHoldColor;
	bool        m_FftFill;

	double       m_PeakDetection;
	QMap<int,int>   m_Peaks;

	QList< QPair<QRect, qint64> >     m_BookmarkTags;

	// Waterfall averaging
	quint64     tlast_wf_ms;        // last time waterfall has been updated
	quint64     msec_per_wfline;    // milliseconds between waterfall updates
	quint64     wf_span;            // waterfall span in milliseconds (0 = auto)
	int         fft_rate;           // expected FFT rate (needed when WF span is auto)

	//Max pixmap size. Without this, for very large screen, update rate will be too slow.
	QSize       m_maxSize;
	double	    m_dPixRatio = 1;
};

#endif // PLOTTER_H
